package com.voice.core.channel.tcp.impl;

import com.voice.core.channel.IOContext;
import com.voice.core.connection.Connection;
import com.voice.core.connection.ConnectionManager;
import com.voice.core.connection.ConnectionStatus;
import com.voice.core.connection.impl.SocketChannelConnectionImpl;
import com.voice.core.utils.CloseUtil;
import com.voice.core.utils.ThreadFactoryUtil;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

public class IOSelectorConnectionManager implements ConnectionManager{

    private AtomicBoolean isRun;
    private IOContext ioContext;
    private List<Connection> connections;
    private PingThread pingThread;

    public IOSelectorConnectionManager() throws IOException {
        this.ioContext = new IOSelectorContext(Executors.newFixedThreadPool(1, ThreadFactoryUtil.threadFactory("IOSelectorContext inputPool")),
                Executors.newFixedThreadPool(1, ThreadFactoryUtil.threadFactory("IOSelectorContext outPool")));
        this.isRun=new AtomicBoolean(true);
        this.connections=new ArrayList<>();
    }

    public IOSelectorConnectionManager(IOContext ioContext) {
        this.ioContext = ioContext;
        this.isRun=new AtomicBoolean(true);
        this.connections=new ArrayList<>();
        this.pingThread=new PingThread("connection-manager-ping-thread");
        this.pingThread.setPriority(Thread.MAX_PRIORITY);
        this.pingThread.start();
    }

    @Override
    public Connection connect(SocketAddress socketAddress) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(socketAddress);
        SocketChannelConnectionImpl connection = new SocketChannelConnectionImpl(socketChannel,ioContext,1);
        connections.add(connection);
        return connection;
    }

    @Override
    public Connection getConnection(SocketChannel socketChannel) {
        SocketChannelConnectionImpl connection = new SocketChannelConnectionImpl(socketChannel,ioContext,2);
        connections.add(connection);
        return connection;
    }

    @Override
    public void shutdown() {
        if(isRun.compareAndSet(true,false)){
            connections.forEach(connection -> connection.close());
            CloseUtil.close(ioContext);
        }
    }


    class PingThread extends Thread{

        private Random random;

        public PingThread(String name){
            super(name);
            this.random=new Random();
        }
        @Override
        public void run() {
            while (isRun.get()){
                for(int i=0;i<connections.size();i++){
                     Connection connection=connections.get(i);
                     if(connection.getStatus()== ConnectionStatus.CLOSED){
                         connections.remove(connection);
                     }
                     connection.writePing(random.nextLong());
                }
                try {
                    Thread.sleep(1000*30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
